/**
 * @file param.c
 * @author your name (you@domain.com)
 * @brief
 * 1. 选取最后2页作为参数存储区域，每页2KB，故一共4096字节；
 * 2. 通过PARAM_MAX_COUNT设置最大参数数量，存储空间限制了参数数量；
 * 3. 参数结构体包含名称、值、校验和、状态标志；共计28字节；
 * 4. 参数系统初始化时，扫描Flash区域查找有效参数；
 * @version 0.1
 * @date 2025-02-24
 *
 * @copyright Copyright (c) 2025
 *
 */
#include <stdint.h>
#include <string.h>
#include "stm32f1xx_hal.h"
#include "param.h"

/* STM32F103VCT6 高容量型号参数 */
#define PARAM_FLASH_START_ADDR 0x08000000 // Flash起始地址
#define PARAM_FLASH_TOTAL_PAGES 128       // Flash总页数
#define PARAM_FLASH_PAGE_SIZE 2048        // 2KB/页

#define PARAM_FLASH_START (PARAM_FLASH_START_ADDR + (PARAM_FLASH_TOTAL_PAGES - 2) * PARAM_FLASH_PAGE_SIZE) // 0x0803F000, 使用最后2页（页126-127）
#define PARAM_FLASH_END 0x08040000                                                                         // 256KB芯片的结束地址
#define PARAM_MAX_COUNT 32                                                                                 // 最大参数数量

/* 参数条目结构体（对齐到4字节），共28字节*/
typedef struct __packed
{
    char name[16];        // 参数名称
    int32_t value;        // 参数值
    uint32_t checksum;    // 校验和（名称+值的异或校验）
    uint32_t status_flag; // 状态标志，0xFFFFFFFF表示有效（还未写入数据）, 0x00000000表示无效（已经写入数据）
} ParamEntry;

/* 全局参数缓存 */
static ParamEntry param_cache[PARAM_MAX_COUNT];
static uint16_t param_count = 0;

/* Flash操作 */
static void flash_erase_page(uint32_t page_address);
static void flash_write(uint32_t address, const void *data, uint32_t size);
static void flash_read(uint32_t address, void *data, uint32_t size);

/* 辅助函数 */
static uint32_t calculate_checksum(const ParamEntry *entry);
static ParamEntry *find_in_cache(const char *name);
static ParamEntry *find_in_flash(const char *name);

// 参数管理初始化（系统启动时调用）
void param_system_init(void)
{
    ParamEntry entry;
    uint32_t address = PARAM_FLASH_START;

    // flash_erase_page(address); // 初始化时先不擦除

    param_count = 0;

    // 扫描Flash区域查找有效参数
    while (address < PARAM_FLASH_END && param_count < PARAM_MAX_COUNT)
    {
        flash_read(address, &entry, sizeof(ParamEntry));

        // 验证条目有效性
        if (entry.status_flag == 0xFFFFFFFF &&
            entry.checksum == calculate_checksum(&entry))
        {
            memcpy(&param_cache[param_count], &entry, sizeof(ParamEntry));
            param_count++;
        }

        address += sizeof(ParamEntry);
    }
}

// 初始化参数（如果不存在则创建）
void param_int32_init(const char *name, int32_t val)
{
    ParamEntry *entry = find_in_cache(name);

    if (entry == NULL)
    {
        // 写入新参数到Flash
        ParamEntry new_entry;
        memset(&new_entry, 0, sizeof(ParamEntry));
        strncpy(new_entry.name, name, 15);
        new_entry.value = val;
        new_entry.checksum = calculate_checksum(&new_entry);
        new_entry.status_flag = 0xFFFFFFFF;

        flash_write(PARAM_FLASH_START + param_count * sizeof(ParamEntry),
                    &new_entry, sizeof(ParamEntry));

        // 更新缓存
        memcpy(&param_cache[param_count], &new_entry, sizeof(ParamEntry));
        param_count++;
    }
}

/**
 * @brief 设置参数值
 * 1. 如果参数存在，则更新参数值；
 * 2. 如果参数不存在，则创建新参数；
 * 3. 如果Flash空间不足，擦除第一页重新写入；
 * 4. 更新参数缓存；
 * 5. 参数值更新后，需要重新写入Flash；
 * 6. 参数值更新后，需要更新缓存；
 *
 * @param name
 * @param val
 */
void param_int32_set(const char *name, int32_t val)
{
    ParamEntry *entry = find_in_cache(name);
    uint32_t write_address;

    if (entry != NULL)
    {
        // 标记旧条目为无效
        entry->status_flag = 0x00000000;
        write_address = PARAM_FLASH_START +
                        ((entry - param_cache) * sizeof(ParamEntry));
        flash_write(write_address + offsetof(ParamEntry, status_flag),
                    &entry->status_flag, sizeof(uint32_t));

        // 创建新条目
        ParamEntry new_entry = *entry;
        new_entry.value = val;
        new_entry.checksum = calculate_checksum(&new_entry);
        new_entry.status_flag = 0xFFFFFFFF;

        // 寻找空闲地址写入（由于写入的位置已经不能重新写入，故往后寻找空闲位置）
        write_address = PARAM_FLASH_START + param_count * sizeof(ParamEntry);
        if (write_address + sizeof(ParamEntry) >= PARAM_FLASH_END)
        {
            flash_erase_page(PARAM_FLASH_START);
            param_count = 0;
            write_address = PARAM_FLASH_START;
        }

        flash_write(write_address, &new_entry, sizeof(ParamEntry));

        // 更新缓存
        *entry = new_entry;
        param_count++;
    }
}

/**
 * @brief 获取参数值
 * 1. 先在缓存中查找，如果找到则返回；
 * 2. 如果缓存中没有，尝试在Flash中查找；
 * 3. 如果Flash中找到，则返回；
 * 4. 如果未找到，则返回最小值；
 */
int32_t param_int32_get(const char *name)
{
    ParamEntry *entry = find_in_cache(name);

    if (entry != NULL)
    {
        return entry->value;
    }

    // 如果缓存中没有，尝试在Flash中查找
    entry = find_in_flash(name);
    if (entry != NULL)
    {
        return entry->value;
    }

    // 未找到参数返回最小值
    return 0x80000000;
}

/* 辅助函数实现 */
static uint32_t calculate_checksum(const ParamEntry *entry)
{
    uint32_t checksum = 0;
    const uint8_t *p = (const uint8_t *)entry;

    for (int i = 0; i < sizeof(entry->name) + sizeof(entry->value); i++)
    {
        checksum ^= p[i];
    }
    return checksum;
}

static ParamEntry *find_in_cache(const char *name)
{
    for (int i = 0; i < param_count; i++)
    {
        if (strncmp(param_cache[i].name, name, 16) == 0 &&
            param_cache[i].status_flag == 0xFFFFFFFF)
        {
            return &param_cache[i];
        }
    }
    return NULL;
}

static ParamEntry *find_in_flash(const char *name)
{
    static ParamEntry entry;
    uint32_t address = PARAM_FLASH_START;

    while (address < PARAM_FLASH_END)
    {
        flash_read(address, &entry, sizeof(ParamEntry));

        if (entry.status_flag == 0xFFFFFFFF &&
            strncmp(entry.name, name, 16) == 0 &&
            entry.checksum == calculate_checksum(&entry))
        {
            return &entry;
        }

        address += sizeof(ParamEntry);
    }
    return NULL;
}

/* Flash底层操作 */
static void flash_erase_page(uint32_t page_address)
{
    FLASH_EraseInitTypeDef erase;
    uint32_t page_error;

    HAL_FLASH_Unlock();

    erase.TypeErase = FLASH_TYPEERASE_PAGES;
    erase.PageAddress = page_address;
    erase.NbPages = 1;

    if (HAL_FLASHEx_Erase(&erase, &page_error) != HAL_OK)
    {
        // Error_Handler();
    }

    HAL_FLASH_Lock();
}

static void flash_write(uint32_t address, const void *data, uint32_t size)
{
    const uint16_t *pdata = (const uint16_t *)data;

    HAL_FLASH_Unlock();

    for (uint32_t i = 0; i < size; i += 2)
    {
        uint16_t val = *pdata++;
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, address + i, val) != HAL_OK)
        {
            // Error_Handler();
        }
    }

    HAL_FLASH_Lock();
}

static void flash_read(uint32_t address, void *data, uint32_t size)
{
    memcpy(data, (void *)address, size);
}

// void Error_Handler(void)
// {
//     while (1)
//         ;
// }

void param_test()
{
    // 初始化参数系统
    param_system_init();

    // 注册所有参数（仅在初始化阶段有效）
    param_int32_init("abc", 0x0101);
    param_int32_init("123", 0x0202);

    //
    int32_t current_speed;
    current_speed = param_int32_get("abc");
    param_int32_set("abc", 0x0303);
    current_speed = param_int32_get("abc");
    param_int32_set("abc", 0x0404);
    current_speed = param_int32_get("abc");
}